/**
 * 请求封装
 * 基于 uni.request 封装，支持拦截器和 Token 自动刷新
 */

import { BASE_URL, REQUEST_TIMEOUT, TOKEN_KEY, USER_INFO_KEY, REFRESH_TOKEN_KEY } from '@/config/env'

/** 请求配置接口*/
interface RequestConfig {
    url: string
    method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
    data?: any
    header?: any
    timeout?: number
    showLoading?: boolean
    loadingText?: string
}

/** 响应接口*/
interface Response<T = any> {
    data: T
    statusCode: number
    header: any
    cookies: string[]
}

/** 是否正在刷新 Token*/
let isRefreshing = false
/** 等待刷新的请求队列*/
let refreshQueue: Array<{
    config: RequestConfig
    resolve: (value: any) => void
    reject: (reason: any) => void
}> = []

/**
 * 获取 Token
 */
const getToken = (): string => {
    return uni.getStorageSync(TOKEN_KEY) || ''
}

/**
 * 设置 Token
 */
const setToken = (token: string): void => {
    uni.setStorageSync(TOKEN_KEY, token)
}

/**
 * 获取 Refresh Token
 */
const getRefreshToken = (): string => {
    return uni.getStorageSync(REFRESH_TOKEN_KEY) || ''
}

/**
 * 设置 Refresh Token
 */
const setRefreshToken = (token: string): void => {
    uni.setStorageSync(REFRESH_TOKEN_KEY, token)
}

/**
 * 处理队列中的请求
 */
const processQueue = (error: any = null) => {
    refreshQueue.forEach(({ config, resolve, reject }) => {
        if (error) {
            reject(error)
        } else {
            request(config).then(resolve).catch(reject)
        }
    })
    refreshQueue = []
}

/**
 * 刷新 Token
 */
const refreshToken = async (): Promise<string> => {
    try {
        const refreshTokenValue = getRefreshToken()

        if (!refreshTokenValue) {
            throw new Error('未找到 Refresh Token，请重新登录')
        }

        const response = await uni.request({
            url: `${BASE_URL}/auth/refresh-token`,
            method: 'POST',
            header: {
                'Content-Type': 'application/json',
                'X-Refresh-Token': refreshTokenValue // 通过请求头发送 refresh token
            },
            data: {
                refreshToken: refreshTokenValue // 也通过请求体发送（双重保障）
            }
        })

        if (response.statusCode === 200 && response.data) {
            const raw = response.data as any
            const payload = raw?.data ?? raw
            const newToken = payload.accessToken

            // 如果返回了新的 refresh token，更新它
            if (payload.refreshToken) {
                setRefreshToken(payload.refreshToken)
            }

            setToken(newToken)
            return newToken
        } else {
            const errorMsg = (response.data as any)?.message || '刷新 Token 失败'
            throw new Error(errorMsg)
        }
    } catch (error: any) {
        throw error
    }
}

/**
 * 统一请求方法
 */
export const request = <T = any>(config: RequestConfig): Promise<T> => {
    return new Promise((resolve, reject) => {
        const {
            url,
            method = 'GET',
            data,
            header = {},
            timeout = REQUEST_TIMEOUT,
            showLoading = false,
            loadingText = '加载中...'
        } = config

        // 显示 Loading
        if (showLoading) {
            uni.showLoading({ title: loadingText, mask: true })
        }

        // 获取 Token
        const token = getToken()
        if (token) {
            header['Authorization'] = `Bearer ${token}`
        }

        // 发起请求
        // 注意：uni.request 的 method 类型不包含 PATCH，需要类型转换
        uni.request({
            url: url.startsWith('http') ? url : `${BASE_URL}${url}`,
            method: method as any, // 类型转换，因为 uni.request 的类型定义不完整
            data,
            header: {
                'Content-Type': 'application/json',
                ...header
            },
            timeout,
            success: (res: Response) => {
                if (showLoading) {
                    uni.hideLoading()
                }

                // 请求成功
                if (res.statusCode >= 200 && res.statusCode < 300) {
                    resolve(res.data as T)
                }
                // Token 过期，尝试刷新
                else if (res.statusCode === 401) {
                    // 如果正在刷新，加入队列
                    if (isRefreshing) {
                        refreshQueue.push({ config, resolve, reject })
                        return
                    }

                    isRefreshing = true

                    // 刷新 Token
                    refreshToken()
                        .then(() => {
                            // 刷新成功，重试原请求
                            request(config).then(resolve).catch(reject)
                            // 处理队列中的请求
                            processQueue()
                        })
                        .catch((error) => {
                            // 刷新失败，清除 Token 并跳转登录页
                            uni.removeStorageSync(TOKEN_KEY)
                            uni.removeStorageSync(USER_INFO_KEY)
                            uni.removeStorageSync(REFRESH_TOKEN_KEY)
                            processQueue(error)

                            uni.showToast({
                                title: '登录已过期，请重新登录',
                                icon: 'none'
                            })

                            setTimeout(() => {
                                uni.reLaunch({
                                    url: '/pages/login/login'
                                })
                            }, 1500)

                            reject(error)
                        })
                        .finally(() => {
                            isRefreshing = false
                        })
                }
                // 其他错误
                else {
                    const rawMsg = (res.data as any)?.message
                    const errorMsg = Array.isArray(rawMsg)
                        ? rawMsg.join('; ')
                        : (rawMsg || '请求失败')
                    uni.showToast({
                        title: String(errorMsg),
                        icon: 'none'
                    })
                    reject(new Error(String(errorMsg)))
                }
            },
            fail: (error) => {
                if (showLoading) {
                    uni.hideLoading()
                }
                uni.showToast({
                    title: '网络请求失败',
                    icon: 'none'
                })

                reject(error)
            }
        })
    })
}

/**
 * GET 请求
 */
export const get = <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<T> => {
    return request<T>({
        url,
        method: 'GET',
        data,
        ...config
    })
}

/**
 * POST 请求
 */
export const post = <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<T> => {
    return request<T>({
        url,
        method: 'POST',
        data,
        ...config
    })
}

/**
 * PUT 请求
 */
export const put = <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<T> => {
    return request<T>({
        url,
        method: 'PUT',
        data,
        ...config
    })
}

/**
 * PATCH 请求
 */
export const patch = <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<T> => {
    return request<T>({
        url,
        method: 'PATCH',
        data,
        ...config
    })
}

/**
 * DELETE 请求
 */
export const del = <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<T> => {
    return request<T>({
        url,
        method: 'DELETE',
        data,
        ...config
    })
}

export default {
    request,
    get,
    post,
    put,
    patch,
    del
}

